//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ArffObject.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fstream>
#include <iostream>
#include <fcntl.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define MAX_SIZE 500000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ArffObject::ArffObject () {

  this->filename                     = new String ();
  this->comment                      = new String ();
  this->relation                     = new String ();
  this->attributes                   = new TListE <ArffAttributeObject> ();
  this->domains                      = new TListE <String> ();
  this->data                         = NULL;
  this->classification               = new TListE <String> ();

  this->toString                     = new String ();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ArffObject::ArffObject (const ArffObject& arffObject) {

  this->filename                     = new String (arffObject.filename);
  this->comment                      = new String (arffObject.comment);
  this->relation                     = new String (arffObject.relation);
  this->attributes                   = new TListE <ArffAttributeObject> (arffObject.attributes);
  this->domains                      = new TListE <String> (arffObject.domains);
  
  int size = this->attributes->Length() - 2;
  this->data                         = new int* [size];
  for (int i = 0; i < size; i++) {
    this->data[i] = new int [size];
    for (int j = 0; j < size; j++)
      this->data[i][j] = arffObject.data[i][j];
  }
  
  this->classification               = new TListE <String> (arffObject.classification);

  this->toString                     = new String (arffObject.toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ArffObject::ArffObject (const ArffObject* arffObject) {

  this->filename                     = new String (arffObject->filename);
  this->comment                      = new String (arffObject->comment);
  this->relation                     = new String (arffObject->relation);
  this->attributes                   = new TListE <ArffAttributeObject> (arffObject->attributes);
  this->domains                      = new TListE <String> (arffObject->domains);

  int size = this->attributes->Length() - 2;
  this->data                         = new int* [size];
  for (int i = 0; i < size; i++) {
    this->data[i] = new int [size];
    for (int j = 0; j < size; j++)
      this->data[i][j] = arffObject->data[i][j];
  }
  
  this->classification               = new TListE <String> (arffObject->classification);

  this->toString                     = new String (arffObject->toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ArffObject::~ArffObject () {

  if (this->data) {
    for (int i = 0; i < this->attributes->Length() - 2; i++)
      if (this->data[i]) delete [] this->data[i];
    delete [] this->data;
  }

  if (this->filename)       delete this->filename;
  if (this->comment)        delete this->comment;
  if (this->relation)       delete this->relation;
  if (this->attributes)     delete this->attributes;
  if (this->domains)        delete this->domains;
  if (this->classification) delete this->classification;
  if (this->toString)       delete this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Filename (String* filename) {

  if (filename)
    *(this->filename) = *filename;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Comment (String* comment) {

  if (comment)
    *(this->comment) = *comment;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Relation (String* relation) {

  if (relation)
    *(this->relation) = *relation;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Attributes (TListE<ArffAttributeObject>* attributes) {

  if (attributes)
    *(this->attributes) = *attributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Domains (TListE<String>* domains) {

  if (domains)
    *(this->domains) = *domains;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Data (int** data, int size) {

  if (data) {
    
    if (this->data) {
      for (int i = 0; i < this->attributes->Length() - 2; i++)
        if (this->data[i]) delete [] this->data[i];
      delete [] this->data;
    }
    
    this->data = new int* [size];
    for (int i = 0; i < size; i++) {
      this->data[i] = new int [size];
      for (int j = 0; j < size; j++)
        this->data[i][j] = data[i][j];
    }

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::Classification (TListE<String>* classification) {

  if (classification)
    *(this->classification) = *classification;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ArffObject::Filename (void) {

  return this->filename;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ArffObject::Comment (void) {

  return this->comment;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ArffObject::Relation (void) {

  return this->relation;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE<ArffAttributeObject>* ArffObject::Attributes (void) {

  return this->attributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE<String>* ArffObject::Domains (void) {

  return this->domains;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int** ArffObject::Data (void) {

  return this->data;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE<String>* ArffObject::Classification (void) {

  return this->classification;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ArffObject::LoadData (void) {

  if (!this->filename || !this->filename->Length()) 
    return false;

  ifstream file (this->filename->Out());
  
  if (!file.is_open())
    return false;

  this->CleanData();

  char  buffer [MAX_SIZE];
  char* parts    = NULL;
  bool  condData = false;
  int   row      = 0;
  long  counter  = 0;
  
  while (!file.eof()) {
    file.getline(buffer, MAX_SIZE);
    counter++;
  }
  
  file.close();
  file.open(this->filename->Out(), ios::in);

  while (!file.eof()) {

    file.getline(buffer, MAX_SIZE);

    if (strlen(buffer) < 2) {
      continue;
      counter--;
    }

    if (buffer[0] == '%') {
      this->comment->Add(buffer);
      counter--;
    }
    else if (strstr(buffer, "@relation")) {
      this->relation->In(buffer);
      counter--;
    }
    else if (strstr(buffer, "@attribute")) {
      
      ArffAttributeObject* newArffAttributeObject = new ArffAttributeObject ();
      
      if (newArffAttributeObject->ReadFromString(buffer))
        this->attributes->Add(newArffAttributeObject);
      else
        delete newArffAttributeObject;
      
      counter--;
      
    }
    else if (strstr(buffer, "@data")) {
      
      counter--;
      
      condData = true;
      
      int size = this->attributes->Length();
      
      this->data = new int* [counter];
      for (int i = 0; i < counter; i++)
        this->data[i] = new int [size];
        
    }
    else if (condData) {

      if (!(parts = strtok(buffer, " ,\n\t\r"))) continue;
      this->domains->Add(new String(parts));

      int  position = 0;

      if (!(parts = strtok(NULL, " ,\n\t\r"))) continue;
      while (parts) {
        if (!strstr(parts, "bind"))
          this->data[row][position++] = atoi(parts); 
         else
          this->classification->Add(new String(parts));
        
        parts = strtok(NULL, " ,\n\t\r");
        
      }
      row++;
      
    }
    else if (condData && strlen(buffer) < 2)
      condData = false;

  }

  file.close();

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ArffObject::CleanData (void) {
  
  if (this->data) {
    for (int i = 0; i < this->attributes->Length() - 2; i++)
      if (this->data[i]) delete [] this->data[i];
    delete [] this->data;
  }

  this->comment->In("");
  this->relation->In("");
  this->attributes->Clear();
  this->domains->Clear();
  this->classification->Clear();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ArffObject ArffObject::operator= (const ArffObject& arffObject) {

  *(this->filename)       = *(arffObject.filename);
  *(this->comment)        = *(arffObject.comment);
  *(this->relation)       = *(arffObject.relation);
  *(this->attributes)     = *(arffObject.attributes);
  *(this->domains)        = *(arffObject.domains);
  
  int size = this->attributes->Length() - 2;
  
  if (this->data) {
    for (int i = 0; i < this->attributes->Length() - 2; i++)
      if (this->data[i]) delete [] this->data[i];
    delete [] this->data;
  }

  this->data = new int* [size];
  for (int i = 0; i < size; i++) {
    this->data[i] = new int [size];
    for (int j = 0; j < size; j++)
      this->data[i][j] = data[i][j];
  }
    
  *(this->classification) = *(arffObject.classification);

  *(this->toString)       = *(arffObject.toString);

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
